﻿// Copyright 2012 Jack Bulan (agentgeo)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace GemWorkshop.Drawing3D
{
    /*
    public class Cube
    {
        //Example cube class to demonstrate the use of 3D points and 3D rotation

        public int width = 0;
        public int height = 0;
        public int depth = 0;

        double xRotation = 0.0;
        double yRotation = 0.0;
        double zRotation = 0.0;

        Camera camera1 = new Camera();
        Point3D cubeOrigin;

        public double RotateX
        {
            get { return xRotation; }
            set { xRotation = value; }
        }

        public double RotateY
        {
            get { return yRotation; }
            set { yRotation = value; }
        }

        public double RotateZ
        {
            get { return zRotation; }
            set { zRotation = value; }
        }

        public Cube(int side)
        {
            width = side;
            height = side;
            depth = side;
            cubeOrigin = new Point3D(width / 2, height / 2, depth / 2);
        }

        public Cube(int side, Point3D origin)
        {
            width = side;
            height = side;
            depth = side;
            cubeOrigin = origin;
        }

        public Cube(int Width, int Height, int Depth)
        {
            width = Width;
            height = Height;
            depth = Depth;
            cubeOrigin = new Point3D(width / 2, height / 2, depth / 2);
        }

        public Cube(int Width, int Height, int Depth, Point3D origin)
        {
            width = Width;
            height = Height;
            depth = Depth;
            cubeOrigin = origin;
        }

        //Finds the othermost points. Used so when the cube is drawn on a bitmap,
        //the bitmap will be the correct size
        public static Rectangle getBounds(PointF[] points)
        {
            double left = points[0].X;
            double right = points[0].X;
            double top = points[0].Y;
            double bottom = points[0].Y;
            for (int i = 1; i < points.Length; i++)
            {
                if (points[i].X < left)
                    left = points[i].X;
                if (points[i].X > right)
                    right = points[i].X;
                if (points[i].Y < top)
                    top = points[i].Y;
                if (points[i].Y > bottom)
                    bottom = points[i].Y;
            }

            return new Rectangle(0, 0, (int)Math.Round(right - left), (int)Math.Round(bottom - top));
        }

        public Bitmap drawCube(Point drawOrigin)
        {
            //FRONT FACE
            //Top Left - 7
            //Top Right - 4
            //Bottom Left - 6
            //Bottom Right - 5

            //Vars
            PointF[] point3D = new PointF[24]; //Will be actual 2D drawing points
            Point tmpOrigin = new Point(0, 0);

            Point3D point0 = new Point3D(0, 0, 0); //Used for reference

            //Zoom factor is set with the monitor width to keep the cube from being distorted
            double zoom = (double)Screen.PrimaryScreen.Bounds.Width / 1.5;

            //Set up the cube
            Point3D[] cubePoints = fillCubeVertices(width, height, depth);

            //Calculate the camera Z position to stay constant despite rotation            
            Point3D anchorPoint = (Point3D)cubePoints[4]; //anchor point
            double cameraZ = -(((anchorPoint.X - cubeOrigin.X) * zoom) / cubeOrigin.X) + anchorPoint.Z;
            camera1.Position = new Point3D(cubeOrigin.X, cubeOrigin.Y, cameraZ);

            //Apply Rotations, moving the cube to a corner then back to middle
            cubePoints = Math3D.Translate(cubePoints, cubeOrigin, point0);
            cubePoints = Math3D.RotateX(cubePoints, xRotation); //The order of these
            cubePoints = Math3D.RotateY(cubePoints, yRotation); //rotations is the source
            cubePoints = Math3D.RotateZ(cubePoints, zRotation); //of Gimbal Lock
            cubePoints = Math3D.Translate(cubePoints, point0, cubeOrigin);

            //Convert 3D Points to 2D
            Point3D vec;
            for (int i = 0; i < point3D.Length; i++)
            {
                vec = cubePoints[i];
                if (vec.Z - camera1.Position.Z >= 0)
                {
                    point3D[i].X = (int)((double)-(vec.X - camera1.Position.X) / (-0.1f) * zoom) + drawOrigin.X;
                    point3D[i].Y = (int)((double)(vec.Y - camera1.Position.Y) / (-0.1f) * zoom) + drawOrigin.Y;
                }
                else
                {
                    tmpOrigin.X = (int)((double)(cubeOrigin.X - camera1.Position.X) / (double)(cubeOrigin.Z - camera1.Position.Z) * zoom) + drawOrigin.X;
                    tmpOrigin.Y = (int)((double)-(cubeOrigin.Y - camera1.Position.Y) / (double)(cubeOrigin.Z - camera1.Position.Z) * zoom) + drawOrigin.Y;

                    point3D[i].X = (float)((vec.X - camera1.Position.X) / (vec.Z - camera1.Position.Z) * zoom + drawOrigin.X);
                    point3D[i].Y = (float)(-(vec.Y - camera1.Position.Y) / (vec.Z - camera1.Position.Z) * zoom + drawOrigin.Y);

                    point3D[i].X = (int)point3D[i].X;
                    point3D[i].Y = (int)point3D[i].Y;
                }
            }

            //Now to plot out the points
            Rectangle bounds = getBounds(point3D);
            bounds.Width += drawOrigin.X;
            bounds.Height += drawOrigin.Y;

            Bitmap tmpBmp = new Bitmap(bounds.Width, bounds.Height);
            Graphics g = Graphics.FromImage(tmpBmp);

            //Back Face
            g.DrawLine(Pens.Red, point3D[0], point3D[1]);
            g.DrawLine(Pens.Red, point3D[1], point3D[2]);
            g.DrawLine(Pens.Red, point3D[2], point3D[3]);
            g.DrawLine(Pens.Red, point3D[3], point3D[0]);

            //Front Face
            g.DrawLine(Pens.Red, point3D[4], point3D[5]);
            g.DrawLine(Pens.Red, point3D[5], point3D[6]);
            g.DrawLine(Pens.Red, point3D[6], point3D[7]);
            g.DrawLine(Pens.Red, point3D[7], point3D[4]);

            //Right Face
            g.DrawLine(Pens.Red, point3D[8], point3D[9]);
            g.DrawLine(Pens.Red, point3D[9], point3D[10]);
            g.DrawLine(Pens.Red, point3D[10], point3D[11]);
            g.DrawLine(Pens.Red, point3D[11], point3D[8]);

            //Left Face
            g.DrawLine(Pens.Red, point3D[12], point3D[13]);
            g.DrawLine(Pens.Red, point3D[13], point3D[14]);
            g.DrawLine(Pens.Red, point3D[14], point3D[15]);
            g.DrawLine(Pens.Red, point3D[15], point3D[12]);

            //Bottom Face
            g.DrawLine(Pens.Red, point3D[16], point3D[17]);
            g.DrawLine(Pens.Red, point3D[17], point3D[18]);
            g.DrawLine(Pens.Red, point3D[18], point3D[19]);
            g.DrawLine(Pens.Red, point3D[19], point3D[16]);

            //Top Face
            g.DrawLine(Pens.Red, point3D[20], point3D[21]);
            g.DrawLine(Pens.Red, point3D[21], point3D[22]);
            g.DrawLine(Pens.Red, point3D[22], point3D[23]);
            g.DrawLine(Pens.Red, point3D[23], point3D[20]);

            g.Dispose(); //Clean-up

            return tmpBmp;
        }

        public Bitmap drawCube(Point3D drawOrigin)
        {
            //Zoom factor is set with the monitor width to keep the cube from being distorted
            double zoom = (double)Screen.PrimaryScreen.Bounds.Width / 1.5;
            
            Point poi = new Point();
            if (drawOrigin.Z - camera1.Position.Z >= 0)
            {
                poi.X = (int)((double)-(drawOrigin.X - camera1.Position.X) / (-0.1f) * zoom);// + drawOrigin.X;
                poi.Y = (int)((double)(drawOrigin.Y - camera1.Position.Y) / (-0.1f) * zoom);// + drawOrigin.Y;
            }
            else
            {
                //tmpOrigin.X = (int)((double)(cubeOrigin.X - camera1.Position.X) / (double)(cubeOrigin.Z - camera1.Position.Z) * zoom) + drawOrigin.X;
                //tmpOrigin.Y = (int)((double)-(cubeOrigin.Y - camera1.Position.Y) / (double)(cubeOrigin.Z - camera1.Position.Z) * zoom) + drawOrigin.Y;

                poi.X = (int)((drawOrigin.X - camera1.Position.X) / (drawOrigin.Z - camera1.Position.Z) * zoom + drawOrigin.X);
                poi.Y = (int)(-(drawOrigin.Y - camera1.Position.Y) / (drawOrigin.Z - camera1.Position.Z) * zoom + drawOrigin.Y);

                //poi.X = (int)poi.X;
                //poi.Y = (int)poi.Y;
            }
            return drawCube(poi);
        }

        public static Point3D[] fillCubeVertices(int width, int height, int depth)
        {
            Point3D[] verts = new Point3D[24];

            //front face
            verts[0] = new Point3D(0, 0, 0);
            verts[1] = new Point3D(0, height, 0);
            verts[2] = new Point3D(width, height, 0);
            verts[3] = new Point3D(width, 0, 0);

            //back face
            verts[4] = new Point3D(0, 0, depth);
            verts[5] = new Point3D(0, height, depth);
            verts[6] = new Point3D(width, height, depth);
            verts[7] = new Point3D(width, 0, depth);

            //left face
            verts[8] = new Point3D(0, 0, 0);
            verts[9] = new Point3D(0, 0, depth);
            verts[10] = new Point3D(0, height, depth);
            verts[11] = new Point3D(0, height, 0);

            //right face
            verts[12] = new Point3D(width, 0, 0);
            verts[13] = new Point3D(width, 0, depth);
            verts[14] = new Point3D(width, height, depth);
            verts[15] = new Point3D(width, height, 0);

            //top face
            verts[16] = new Point3D(0, height, 0);
            verts[17] = new Point3D(0, height, depth);
            verts[18] = new Point3D(width, height, depth);
            verts[19] = new Point3D(width, height, 0);

            //bottom face
            verts[20] = new Point3D(0, 0, 0);
            verts[21] = new Point3D(0, 0, depth);
            verts[22] = new Point3D(width, 0, depth);
            verts[23] = new Point3D(width, 0, 0);

            return verts;
        }
    }
    */

    public class Cube
    {
        //Cube face, has four points, 3D and 2D
        internal class Face : IComparable<Face>
        {
            public enum Side
            {
                Front,
                Back,
                Left,
                Right,
                Top,
                Bottom
            }

            public PointF[] Corners2D;
            public Point3D[] Corners3D;
            public Point3D Center;
            public Side CubeSide;

            public Face()
            {
            }

            public int CompareTo(Face otherFace)
            {
                return (int)(this.Center.Z - otherFace.Center.Z); //In order of which is closest to the screen
            }
        }

        public int width = 0;
        public int height = 0;
        public int depth = 0;

        float xRotation = 0.0f;
        float yRotation = 0.0f;
        float zRotation = 0.0f;

        bool drawWires = true;
        bool fillFront;
        bool fillBack;
        bool fillLeft;
        bool fillRight;
        bool fillTop;
        bool fillBottom;

        Point3D cubeOrigin;

        Face[] faces;

        #region Properties

        public float RotateX
        {
            get { return xRotation; }
            set
            {
                //rotate the difference between this rotation and last rotation
                RotateCubeX(value - xRotation);
                xRotation = value;
            }
        }

        public float RotateY
        {
            get { return yRotation; }
            set
            {
                RotateCubeY(value - yRotation);
                yRotation = value;
            }
        }

        public float RotateZ
        {
            get { return zRotation; }
            set
            {
                RotateCubeZ(value - zRotation);
                zRotation = value;
            }
        }

        public bool DrawWires
        {
            get { return drawWires; }
            set { drawWires = value; }
        }

        public bool FillFront
        {
            get { return fillFront; }
            set { fillFront = value; }
        }

        public bool FillBack
        {
            get { return fillBack; }
            set { fillBack = value; }
        }

        public bool FillLeft
        {
            get { return fillLeft; }
            set { fillLeft = value; }
        }

        public bool FillRight
        {
            get { return fillRight; }
            set { fillRight = value; }
        }

        public bool FillTop
        {
            get { return fillTop; }
            set { fillTop = value; }
        }

        public bool FillBottom
        {
            get { return fillBottom; }
            set { fillBottom = value; }
        }
        #endregion

        #region Initializers
        public Cube(int side)
        {
            width = side;
            height = side;
            depth = side;
            cubeOrigin = new Point3D(width / 2, height / 2, depth / 2);
            InitializeCube();
        }

        public Cube(int side, Point3D origin)
        {
            width = side;
            height = side;
            depth = side;
            cubeOrigin = origin;

            InitializeCube();
        }

        public Cube(int Width, int Height, int Depth)
        {
            width = Width;
            height = Height;
            depth = Depth;
            cubeOrigin = new Point3D(width / 2, height / 2, depth / 2);

            InitializeCube();
        }

        public Cube(int Width, int Height, int Depth, Point3D origin)
        {
            width = Width;
            height = Height;
            depth = Depth;
            cubeOrigin = origin;

            InitializeCube();
        }
        #endregion

        private void InitializeCube()
        {
            //Fill in the cube

            faces = new Face[6]; //cube has 6 faces

            //Front Face --------------------------------------------
            faces[0] = new Face();
            faces[0].CubeSide = Face.Side.Front;
            faces[0].Corners3D = new Point3D[4];
            faces[0].Corners3D[0] = new Point3D(0, 0, 0);
            faces[0].Corners3D[1] = new Point3D(0, height, 0);
            faces[0].Corners3D[2] = new Point3D(width, height, 0);
            faces[0].Corners3D[3] = new Point3D(width, 0, 0);
            faces[0].Center = new Point3D(width / 2, height / 2, 0);
            // -------------------------------------------------------

            //Back Face --------------------------------------------
            faces[1] = new Face();
            faces[1].CubeSide = Face.Side.Back;
            faces[1].Corners3D = new Point3D[4];
            faces[1].Corners3D[0] = new Point3D(0, 0, depth);
            faces[1].Corners3D[1] = new Point3D(0, height, depth);
            faces[1].Corners3D[2] = new Point3D(width, height, depth);
            faces[1].Corners3D[3] = new Point3D(width, 0, depth);
            faces[1].Center = new Point3D(width / 2, height / 2, depth);
            // -------------------------------------------------------

            //Left Face --------------------------------------------
            faces[2] = new Face();
            faces[2].CubeSide = Face.Side.Left;
            faces[2].Corners3D = new Point3D[4];
            faces[2].Corners3D[0] = new Point3D(0, 0, 0);
            faces[2].Corners3D[1] = new Point3D(0, 0, depth);
            faces[2].Corners3D[2] = new Point3D(0, height, depth);
            faces[2].Corners3D[3] = new Point3D(0, height, 0);
            faces[2].Center = new Point3D(0, height / 2, depth / 2);
            // -------------------------------------------------------

            //Right Face --------------------------------------------
            faces[3] = new Face();
            faces[3].CubeSide = Face.Side.Right;
            faces[3].Corners3D = new Point3D[4];
            faces[3].Corners3D[0] = new Point3D(width, 0, 0);
            faces[3].Corners3D[1] = new Point3D(width, 0, depth);
            faces[3].Corners3D[2] = new Point3D(width, height, depth);
            faces[3].Corners3D[3] = new Point3D(width, height, 0);
            faces[3].Center = new Point3D(width, height / 2, depth / 2);
            // -------------------------------------------------------

            //Top Face --------------------------------------------
            faces[4] = new Face();
            faces[4].CubeSide = Face.Side.Top;
            faces[4].Corners3D = new Point3D[4];
            faces[4].Corners3D[0] = new Point3D(0, 0, 0);
            faces[4].Corners3D[1] = new Point3D(0, 0, depth);
            faces[4].Corners3D[2] = new Point3D(width, 0, depth);
            faces[4].Corners3D[3] = new Point3D(width, 0, 0);
            faces[4].Center = new Point3D(width / 2, 0, depth / 2);
            // -------------------------------------------------------

            //Bottom Face --------------------------------------------
            faces[5] = new Face();
            faces[5].CubeSide = Face.Side.Bottom;
            faces[5].Corners3D = new Point3D[4];
            faces[5].Corners3D[0] = new Point3D(0, height, 0);
            faces[5].Corners3D[1] = new Point3D(0, height, depth);
            faces[5].Corners3D[2] = new Point3D(width, height, depth);
            faces[5].Corners3D[3] = new Point3D(width, height, 0);
            faces[5].Center = new Point3D(width / 2, height, depth / 2);
            // -------------------------------------------------------
        }

        //Calculates the 2D points of each face
        private void Update2DPoints(Point drawOrigin)
        {
            //Update the 2D points of all the faces
            for (int i = 0; i < faces.Length; i++)
            {
                Update2DPoints(drawOrigin, i);
            }
        }

        private void Update2DPoints(Point drawOrigin, int faceIndex)
        {
            //Calculates the projected coordinates of the 3D points in a cube face
            PointF[] point2D = new PointF[4];
            float zoom = (float)Screen.PrimaryScreen.Bounds.Width / 1.5f;
            Point tmpOrigin = new Point(0, 0);

            //Convert 3D Points to 2D
            Point3D vec;
            for (int i = 0; i < point2D.Length; i++)
            {
                vec = faces[faceIndex].Corners3D[i];
                point2D[i] = Get2D(vec, drawOrigin);
            }

            //Update face
            faces[faceIndex].Corners2D = point2D;
        }

        //Rotating methods, has to translate the cube to the rotation point (center), rotate, and translate back

        private void RotateCubeX(float deltaX)
        {
            for (int i = 0; i < faces.Length; i++)
            {
                //Apply rotation
                //------Rotate points
                Point3D point0 = new Point3D(0, 0, 0);
                faces[i].Corners3D = Math3D.Translate(faces[i].Corners3D, cubeOrigin, point0); //Move corner to origin
                faces[i].Corners3D = Math3D.RotateX(faces[i].Corners3D, deltaX);
                faces[i].Corners3D = Math3D.Translate(faces[i].Corners3D, point0, cubeOrigin); //Move back

                //-------Rotate center
                faces[i].Center = Math3D.Translate(faces[i].Center, cubeOrigin, point0);
                faces[i].Center = Math3D.RotateX(faces[i].Center, deltaX);
                faces[i].Center = Math3D.Translate(faces[i].Center, point0, cubeOrigin);
            }
        }

        private void RotateCubeY(float deltaY)
        {
            for (int i = 0; i < faces.Length; i++)
            {
                //Apply rotation
                //------Rotate points
                Point3D point0 = new Point3D(0, 0, 0);
                faces[i].Corners3D = Math3D.Translate(faces[i].Corners3D, cubeOrigin, point0); //Move corner to origin
                faces[i].Corners3D = Math3D.RotateY(faces[i].Corners3D, deltaY);
                faces[i].Corners3D = Math3D.Translate(faces[i].Corners3D, point0, cubeOrigin); //Move back

                //-------Rotate center
                faces[i].Center = Math3D.Translate(faces[i].Center, cubeOrigin, point0);
                faces[i].Center = Math3D.RotateY(faces[i].Center, deltaY);
                faces[i].Center = Math3D.Translate(faces[i].Center, point0, cubeOrigin);
            }
        }

        private void RotateCubeZ(float deltaZ)
        {
            for (int i = 0; i < faces.Length; i++)
            {
                //Apply rotation
                //------Rotate points
                Point3D point0 = new Point3D(0, 0, 0);
                faces[i].Corners3D = Math3D.Translate(faces[i].Corners3D, cubeOrigin, point0); //Move corner to origin
                faces[i].Corners3D = Math3D.RotateZ(faces[i].Corners3D, deltaZ);
                faces[i].Corners3D = Math3D.Translate(faces[i].Corners3D, point0, cubeOrigin); //Move back

                //-------Rotate center
                faces[i].Center = Math3D.Translate(faces[i].Center, cubeOrigin, point0);
                faces[i].Center = Math3D.RotateZ(faces[i].Center, deltaZ);
                faces[i].Center = Math3D.Translate(faces[i].Center, point0, cubeOrigin);
            }
        }

        public Bitmap DrawCube(Point drawOrigin)
        {
            //Get the corresponding 2D
            Update2DPoints(drawOrigin);

            //Get the bounds of the final bitmap
            Rectangle bounds = getDrawingBounds();
            bounds.Width += drawOrigin.X;
            bounds.Height += drawOrigin.Y;

            Bitmap finalBmp = new Bitmap(bounds.Width, bounds.Height);
            Graphics g = Graphics.FromImage(finalBmp);

            g.SmoothingMode = SmoothingMode.AntiAlias;

            Array.Sort(faces); //sort faces from closets to farthest
            //message();
            for (int i = faces.Length - 1; i >= 0; i--) //draw faces from back to front
            {
                switch (faces[i].CubeSide)
                {
                    case Face.Side.Front:
                        if (fillFront)
                            g.FillPolygon(Brushes.LightGray, GetFrontFace());
                        break;
                    case Face.Side.Back:
                        if (fillBack)
                            g.FillPolygon(Brushes.Gray, GetBackFace());
                        break;
                    case Face.Side.Left:
                        if (fillLeft)
                            g.FillPolygon(Brushes.LightGray, GetLeftFace());
                        break;
                    case Face.Side.Right:
                        if (fillRight)
                            g.FillPolygon(Brushes.Gray, GetRightFace());
                        break;
                    case Face.Side.Top:
                        if (fillTop)
                            g.FillPolygon(Brushes.LightGray, GetTopFace());
                        break;
                    case Face.Side.Bottom:
                        if (fillBottom)
                            g.FillPolygon(Brushes.Gray, GetBottomFace());
                        break;
                    default:
                        break;
                }

                if (drawWires)
                {
                    g.DrawLine(Pens.Black, faces[i].Corners2D[0], faces[i].Corners2D[1]);
                    g.DrawLine(Pens.Black, faces[i].Corners2D[1], faces[i].Corners2D[2]);
                    g.DrawLine(Pens.Black, faces[i].Corners2D[2], faces[i].Corners2D[3]);
                    g.DrawLine(Pens.Black, faces[i].Corners2D[3], faces[i].Corners2D[0]);
                }
            }

            g.Dispose();

            return finalBmp;
        }

        //Converts 3D points to 2D points
        private PointF Get2D(Point3D vec, Point drawOrigin)
        {
            PointF point2D = Get2D(vec);
            return new PointF(point2D.X + drawOrigin.X, point2D.Y + drawOrigin.Y);
        }

        private PointF Get2D(Point3D vec)
        {
            PointF returnPoint = new PointF();

            float zoom = (float)Screen.PrimaryScreen.Bounds.Width / 1.5f;
            Camera tempCam = new Camera();

            tempCam.Position.X = cubeOrigin.X;
            tempCam.Position.Y = cubeOrigin.Y;
            tempCam.Position.Z = (cubeOrigin.X * zoom) / cubeOrigin.X;

            float zValue = (float)(-vec.Z - tempCam.Position.Z);

            returnPoint.X = (float)((tempCam.Position.X - vec.X) / zValue * zoom);
            returnPoint.Y = (float)((tempCam.Position.Y - vec.Y) / zValue * zoom);

            return returnPoint;
        }

        public PointF[] GetFrontFace()
        {
            //Returns the four points corresponding to the front face
            //Get the corresponding 2D
            return getFace(Face.Side.Front).Corners2D;
        }

        public PointF[] GetBackFace()
        {
            return getFace(Face.Side.Back).Corners2D;
        }

        public PointF[] GetRightFace()
        {
            return getFace(Face.Side.Right).Corners2D;
        }

        public PointF[] GetLeftFace()
        {
            return getFace(Face.Side.Left).Corners2D;
        }

        public PointF[] GetTopFace()
        {
            return getFace(Face.Side.Top).Corners2D;
        }

        public PointF[] GetBottomFace()
        {
            return getFace(Face.Side.Bottom).Corners2D;
        }

        private Face getFace(Face.Side side)
        {
            //Find the correct side
            //Since faces are sorted in order of closest to farthest
            //They won't always be in the same index
            for (int i = 0; i < faces.Length; i++)
            {
                if (faces[i].CubeSide == side)
                    return faces[i];
            }

            return null; //not found
        }

        private Rectangle getDrawingBounds()
        {
            //Find the farthest most points to calculate the size of the returning bitmap
            float left = float.MaxValue;
            float right = float.MinValue;
            float top = float.MaxValue;
            float bottom = float.MinValue;

            for (int i = 0; i < faces.Length; i++)
            {
                for (int j = 0; j < faces[i].Corners2D.Length; j++)
                {
                    if (faces[i].Corners2D[j].X < left)
                        left = faces[i].Corners2D[j].X;
                    if (faces[i].Corners2D[j].X > right)
                        right = faces[i].Corners2D[j].X;
                    if (faces[i].Corners2D[j].Y < top)
                        top = faces[i].Corners2D[j].Y;
                    if (faces[i].Corners2D[j].Y > bottom)
                        bottom = faces[i].Corners2D[j].Y;
                }
            }

            return new Rectangle(0, 0, (int)Math.Round(right - left), (int)Math.Round(bottom - top));
        }
    }
}
